﻿using System;
using Sissonix.Infrastructure.DomainBase;
using System.Collections.Generic;
using System.Data.Objects.DataClasses;

namespace Sissonix.Infrastructure.RepositoryFramework
{
    public abstract class RepositoryBase<T> : IRepository<T>, IUnitOfWorkRepository where T : IAggregateRoot
    {
        #region **====================** Fields **====================**
        protected IUnitOfWork unitOfWork;
        #endregion

        #region **====================** Constructors **====================**
        protected RepositoryBase()
        {
        }

        protected RepositoryBase(IUnitOfWork unitOfWork) : this()
        {
            //It's important to call the property rather than setting the
            //variable directly.  The setter property could be overridden
            //(as is the case of EFRepositoryBase) to do special things.
            //The EFRepositoryBase set an EF ObjectContext every time the 
            //unitOfWork changes.
            this.UnitOfWork = unitOfWork;
        }

        #endregion

        #region **====================** IRepository<T> Members **====================**
        public abstract T FindBy(object key);
        public abstract int Count();
        public abstract IList<T> FindAll();
        public abstract IList<T> FindAll(int? start, int? limit, string sortField, string sortDirection);

        public void Add(T item)
        {
            //**** Setup UOW ****
            bool needTempUnitOfWork = (this.unitOfWork == null);
            if (needTempUnitOfWork)
                this.unitOfWork = new UnitOfWork();

            //**** Do CRUD Work ****
            this.unitOfWork.RegisterAdded(item, this);

            //**** Finish UOW ****
            if (needTempUnitOfWork)
            {
                this.unitOfWork.Commit();
                this.unitOfWork = null;
            }

        }

        public void Remove(long id)
        {
            T record = FindBy(id);
            Remove(record);
        }
        
        public void Remove(T item)
        {
            //**** Setup UOW ****
            bool needTempUnitOfWork = (this.unitOfWork == null);
            if (needTempUnitOfWork)
                this.unitOfWork = new UnitOfWork();

            //**** Do CRUD Work ****
            this.unitOfWork.RegisterRemoved(item, this);

            //**** Finish UOW ****
            if (needTempUnitOfWork)
            {
                this.unitOfWork.Commit();
                this.unitOfWork = null;
            }
        }

        public void Change(T item)
        {
            //**** Setup UOW ****
            bool needTempUnitOfWork = (this.unitOfWork == null);
            if (needTempUnitOfWork)
                this.unitOfWork = new UnitOfWork();

            //**** Do CRUD Work ****
            this.unitOfWork.RegisterChanged(item, this);

            //**** Finish UOW ****
            if (needTempUnitOfWork)
            {
                this.unitOfWork.Commit();
                this.unitOfWork = null;
            }
        }
        
        public T this[object key]
        {
            get
            {
                return this.FindBy(key);
            }
            set
            {
                if (this.FindBy(key) == null)
                {
                    if (value != null)
                        this.Add(value);
                }
                else
                {
                    if (value == null)
                        this.Remove(value);
                    else
                        this.Change(value);
                }
            }
        }
        #endregion

        #region **====================** IUnitOfWorkRepository Members **====================**
        public virtual void PersistNewItem(IAggregateRoot item)
        {
            this.PersistNewItem((T)item);
        }

        public virtual void PersistUpdatedItem(IAggregateRoot item)
        {
            this.PersistUpdatedItem((T)item);
        }

        public virtual void PersistDeletedItem(IAggregateRoot item)
        {
            this.PersistDeletedItem((T)item);
        }

        public virtual void CommitPersistedItems()
        {
            this.CommitPersistedItems();
        }
        #endregion

        #region **====================** Properties **====================**
        public virtual IUnitOfWork UnitOfWork
        {
            get { return this.unitOfWork; }
            set { this.unitOfWork = value; }
        }
        #endregion

        #region **====================** Abstract Methods **====================**
        protected abstract void PersistNewItem(T item);
        protected abstract void PersistUpdatedItem(T item);
        protected abstract void PersistDeletedItem(T item);
        #endregion
    }
}
